#pragma once
#define THREADPOOL_H 
#ifdef THREADPOOL_H

#include <vector>
#include <iostream>
#include <queue>
#include <memory>
#include <atomic>
#include <functional>
#include <mutex>
#include <thread>
#include <future>
#include <condition_variable>
#include <unordered_map>

const int TASK_MAX_THRESHHOLD = 1024;
const int THREAD_MAX_THRESHHOLD = 10;
const int THREAD_MAX_IDLE_TIME = 10;

enum class PoolMode     // 线程池模式
{
    MODE_FIXED,     // 固定数量
    MODE_CACHED     // 动态增长
};
 
class Thread
{
public:
    using ThreadFunc = std::function<void(int)>;
    Thread(ThreadFunc func)
        : func_(func)
        , threadId_(generateId_++)
    {} 
    ~Thread() = default;
    void start()   // 启动线程
    {
        std::thread t(func_, threadId_);   // 创建线程
        t.detach();     // 分离线程
    } 
    int getId() const { return threadId_; }
private:
    ThreadFunc func_;
    static int generateId_;
    int threadId_;  
};
int Thread::generateId_ = 0;

class ThreadPool
{
public:
    ThreadPool()
        : initThreadSize_(0)
        , curThreadSize_(0)
        , taskSize_(0)
        , taskQueMaxThreshHold_(TASK_MAX_THRESHHOLD)
        , poolMode_(PoolMode::MODE_FIXED)
        , isRunning_(false)
        , idleThreadSize_(0)
        , threadSizeThreshHold_(100)
    {}
    ~ThreadPool()
    {
        isRunning_ = false;
        // notEmpty_.notify_all();     // 通知阻塞在notEmpty_条件的线程(死锁位置)

        // 等待线程池中所有线程返回：1.阻塞中  2.执行任务中
        std::unique_lock<std::mutex> lock(taskQueMtx_);
        notEmpty_.notify_all();     // 通知阻塞在notEmpty_条件的线程
        exitCond_.wait(lock, [&]()->bool { return threads_.size() == 0; });
    }

    void setMode(PoolMode mode)    // 设置线程池工作模式
    {
        if(checkRunningState()) return;
        poolMode_ = mode;
    }

    void setThreadSizeThreshHold(size_t threshhold)    // Cache模式上限阈值
    {
        if(checkRunningState()) return;
        taskQueMaxThreshHold_ = threshhold;
    }

    void setTaskQueMaxThreshHold(size_t threshhold)    // 设置任务队列阈值
    {
        if(checkRunningState()) return;
        if(poolMode_ == PoolMode::MODE_CACHED) 
            threadSizeThreshHold_ = threshhold;
    }

    // 使用可变参模板来接收任意任务函数和任意数量的参数
    // pool.submitTask(func, a, b);
    template<class Func, class...Args>                                                      /* future<返回值>, 但Func表示的是整个函数接口的类型，*/          
    auto submitTask(Func&& func, Args&&...args) -> std::future<decltype(func(args...))>     /* 因此需要引入decltype，传入函数对象及其参数推到出函数的返回值 */                                                                                
    {
        // 打包任务，push到队列
        using RType = decltype(func(args...));      // 接收返回值
        auto task = std::make_shared<std::packaged_task<RType()>>(              /* 通过make_shared管理函数对象，该指针的类型是一个函数对象 */
            std::bind(std::forward<Func>(func), std::forward<Args>(args)...)   /* 指向的是 func，其参数为 args... */
        );
        std::future<RType> result = task->get_future();
 
        // 加锁保证push任务
        std::unique_lock<std::mutex> lock(taskQueMtx_);

        // 检查合法性，等待队列有空余
        // while (taskQue_.size() == taskQueMaxThreshHold_)
        //     notFull_.wait(lock);

        // 非notFull状态则阻塞，但不能一直阻塞，一定时间后队列仍满，则反馈提交失败
        if(!notFull_.wait_for(lock, std::chrono::seconds(1), 
            [&]()->bool { return taskQue_.size() < taskQueMaxThreshHold_; }))
        {
            std::cerr << "taskQueue is full, task submit fail\n";
            // 任务提交失败，返回该任务返回值的零值（例如int())
            auto task = std::make_shared<std::packaged_task<RType()>>([]()->RType { return RType(); });   
            (*task)();
            return task->get_future(); 
        }
        
        // 有空余直接push
        // taskQue_.emplace(sp);
        taskQue_.emplace([task](){ (*task)(); });
        taskSize_++;

        // push task后，队列不为空，通知消费者线程
        notEmpty_.notify_all();

        // Cache模式下(小而快的任务)：根据任务量和空闲线程的数量，来决定是否创建更多的线程
        if(poolMode_ == PoolMode::MODE_CACHED &&
            taskSize_ > idleThreadSize_ && curThreadSize_ < threadSizeThreshHold_)
        {
            std::cout << "create new thread[" << std::this_thread::get_id() << "]\n";

            std::unique_ptr<Thread> ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
            // threads_.emplace_back(move(ptr));
            int threadId = ptr->getId();
            threads_.emplace(threadId, std::move(ptr));
            threads_[threadId]->start();    // 启动线程
            curThreadSize_++;
            idleThreadSize_++;
        }

        return result;    
    }

    void start(size_t initThreadSize = std::thread::hardware_concurrency())   // 启动线程池
    {
        isRunning_ = true;      // 修改线程池状态

        initThreadSize_ = initThreadSize;
        curThreadSize_ = initThreadSize;
        
        // 创建线程
        for(int i = 0; i < initThreadSize_; ++i)
        {
            std::unique_ptr<Thread> ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this, std::placeholders::_1));
            // std::unique_ptr<Thread> ptr = std::make_unique<Thread>(std::bind(&ThreadPool::threadFunc, this));
            // threads_.emplace_back(move(ptr));
            int threadId = ptr->getId();
            threads_.emplace(threadId, std::move(ptr));
        }

        // 启动线程
        for(int i = 0; i < initThreadSize_; ++i)
        {
            threads_[i]->start();
            idleThreadSize_++;
        }
    }

    ThreadPool(const ThreadPool&) = delete;
    ThreadPool& operator=(const ThreadPool&) = delete; 
private:
    void threadFunc(int threadId) 
    {
        auto lastTime = std::chrono::high_resolution_clock().now();

        // 线程不断的执行任务
        for(;;)
        {
            Task task;
            {
                // 加锁保证获取task时的线程安全
                std::unique_lock<std::mutex> lock(taskQueMtx_);

                std::cout << "尝试获取任务: " << std::this_thread::get_id() << std::endl;

                while (taskSize_ == 0)    // 锁 + 双重判断
                {
                    // 没有任务且线程池析构时再回收每个线程
                    if(!isRunning_)   
                    {
                        threads_.erase(threadId);
                        exitCond_.notify_all();
                        std::cout << "threadId: [" << std::this_thread::get_id() << "] exit!\n";
                        return;
                    }               

                    // Cache模式下：如果线程空余超过60s，回收线程
                    if(poolMode_ == PoolMode::MODE_CACHED)
                    {
                        // 等待任务队列不为空时超时返回，每一秒钟返回一次 
                        if(std::cv_status::timeout == notEmpty_.wait_for(lock, std::chrono::seconds(1)))
                        {
                            auto now = std::chrono::high_resolution_clock().now();
                            auto dur = std::chrono::duration_cast<std::chrono::seconds>(now - lastTime);
                            if(dur.count() >= THREAD_MAX_IDLE_TIME &&
                                curThreadSize_ > initThreadSize_)
                            {
                                // 回收线程 -> 从容器中删除线程 -> 
                                // vector没办法在threadFunc()中定位具体的线程 --> unordered_map
                                threads_.erase(threadId);       // 根据id删线程
                                curThreadSize_--;
                                idleThreadSize_--;
                                std::cout << "threadId: [" << std::this_thread::get_id() << "] exit!\n";
                                return;
                            }
                        }
                    }
                    else notEmpty_.wait(lock);      // 没有任务时阻塞              
                }
    
                idleThreadSize_--;      // 线程被唤醒、空闲-1

                // 取任务
                task = taskQue_.front();
                taskQue_.pop();
                taskSize_--;

                std::cout << "获取任务成功: " << std::this_thread::get_id() << std::endl;

                // 若还有其它任务，通知其它线程
                if(taskQue_.size() > 0) notEmpty_.notify_all();
                // 取出一个任务后，队列不为满，可以提交任务
                notFull_.notify_all();
            }
            // 执行任务
            if(task != nullptr)
            {
                // task->exec();     // 把任务的返回值设置到Result中，再执行任务
                task();
            }
            idleThreadSize_++;
            lastTime = std::chrono::high_resolution_clock().now();  
        }
    }

    bool checkRunningState() const { return isRunning_; }
private:
    std::unordered_map<int, std::unique_ptr<Thread>> threads_;
    size_t initThreadSize_;         // 线程数量
    size_t threadSizeThreshHold_;   // 线程数量上限阈值
    std::atomic_int curThreadSize_;     // 当前数量
    std::atomic_int idleThreadSize_;    // 空闲线程数量

    // std::queue<std::shared_ptr<Task>> taskQue_;     // 任务队列
    using Task = std::function<void()>;     // 因为定义任务对象类型时无法确定任务的返回值 ——> void 
    std::queue<Task> taskQue_;
    std::atomic_uint taskSize_;      // 任务数量
    size_t taskQueMaxThreshHold_;    // 任务数量上限阈值

    std::mutex taskQueMtx_;     // 保证任务队列的线程安全
    std::condition_variable notFull_;   // 表任务队列不满
    std::condition_variable notEmpty_;  // 表任务队列不空
    std::condition_variable exitCond_;  // 等待线程资源回收

    PoolMode poolMode_;
    std::atomic_bool isRunning_;        // 线程池状态
};

#endif
